﻿using Rhino;
using Rhino.UI;
using Rhino.Commands;
using Rhino.DocObjects;
using Rhino.Geometry;
using Rhino.Input;
using Rhino.Input.Custom;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using YangTools.RhinoHelper;
using Eto.Forms;

namespace YangTools.Realization
{
    class BlockEdit
    {
        /// <summary>
        /// 按照名字分类记录图块
        /// </summary>
        private Dictionary<string, List<InstanceObject>> blockTypes = new Dictionary<string, List<InstanceObject>>();

        /// <summary>
        /// 记录生成的新的图块
        /// </summary>
        private List<Guid> newBlocks = new List<Guid>();

        /// <summary>
        /// 获取当前文档空间中的所有的图块定义的名字
        /// </summary>
        private List<string> blockNames
        {
            get
            {
                var instanceDefinitions = RhinoDoc.ActiveDoc.InstanceDefinitions;
                return instanceDefinitions.Select(i => i.Name).ToList();
            }
        }

        /// <summary>
        /// 将图块设置为唯一
        /// </summary>
        internal void MakeUnique()
        {
            // 1、选择需要操作的图块
            var blockObjects = RhinoHelper.Selection.GetObjects("请选择需要设置为唯一的图块：", ObjectType.InstanceReference, 1, 0);

            // 2、统计图块的类型
            ClassifyBlockByDefinition(blockObjects);

            // 开始对每一个图块进行处理
            foreach (var keyValuePair in blockTypes)
            {
                foreach (var block in keyValuePair.Value)
                {
                    int index = RhinoHelper.Block.CopyBlock(block);
                    Transform blockTransform = block.InstanceXform;

                    // 插入图块到文档中，并使用原来的变形与属性
                    Guid newBlock = RhinoDoc.ActiveDoc.Objects.AddInstanceObject(index, blockTransform, block.Attributes);
                    newBlocks.Add(newBlock);
                }
            }

            RhinoDoc.ActiveDoc.Objects.Delete(blockObjects.Select(b => b.Id).ToArray(), false);
            RhinoDoc.ActiveDoc.Objects.UnselectAll();
            RhinoDoc.ActiveDoc.Objects.Select(newBlocks);
        }

        /// <summary>
        /// 恢复经过 _BlockUnique 的图块
        /// </summary>
        internal void UndoUnique()
        {
            // 1、选择需要操作的图块
            var blockObjects = RhinoHelper.Selection.GetObjects("请选择需要尝试撤销 _BlockUnique 的图块：", ObjectType.InstanceReference, 1, 0);

            // 2、统计图块的类型
            ClassifyBlockByDefinition(blockObjects);

            foreach (var keyValuePair in blockTypes)
            {
                #region 找到原始图块的名字
                string name = keyValuePair.Key;
                // 名字里没有“#”说明不是使用 _BlockUnique 命令创建的图块，无法操作
                if (!name.Contains("#"))
                {
                    // 从选择集中删除，避免最后删除原始物体时被删除
                    foreach (var o in keyValuePair.Value)
                    {
                        blockObjects.Remove(o);
                    }
                    continue;
                }

                List<string> partName = name.Split('#').ToList(); // 图块可能经过了多次 _BlockUnique
                partName.RemoveAt(partName.Count - 1);
                string originalName = string.Join("#", partName);
                #endregion

                // 找到原始图块的索引，如果没有就新创建一个
                int originalBlockIndex;
                if (blockNames.Contains(originalName))
                {
                    originalBlockIndex = RhinoDoc.ActiveDoc.InstanceDefinitions.Find(originalName).Index;
                }
                else
                {
                    originalBlockIndex = RhinoHelper.Block.DuplicateBlock(keyValuePair.Value[0], originalName);
                }

                // 插入原始图块
                foreach (var block in keyValuePair.Value)
                {
                    Transform blockTransform = block.InstanceXform;
                    Guid newBlock = RhinoDoc.ActiveDoc.Objects.AddInstanceObject(originalBlockIndex, blockTransform, block.Attributes);
                    newBlocks.Add(newBlock);
                }
            }

            RhinoDoc.ActiveDoc.Objects.Delete(blockObjects.Select(b => b.Id).ToArray(), false);
            RhinoDoc.ActiveDoc.Objects.UnselectAll();
            RhinoDoc.ActiveDoc.Objects.Select(newBlocks);
        }

        /// <summary>
        /// 增强编辑图块，可以编辑等比缩放和不等比缩放的图块
        /// </summary>
        internal void BlockEditPlus()
        {
            // 1、选择需要操作的图块
            var blockToAdd = Selection.GetBlocks("选择要操作的图块：", 1, -1);
            if (blockToAdd.Count == 0) return;
            var block = blockToAdd[0];


            // 2、隐藏操作图块，锁定其他所有未锁定的物体，并显示图块的子物体
            block.Attributes.Visible = false;
            block.CommitChanges();

            // ****锁定所有物体，此时还没有添加图块里的物体，所以直接获取全部物体就是其他所有物体；同时区分原本就是锁定的物体，就可以直接使用这个列表来进行编辑结束后解锁物体
            var objectsOutBlock = RhinoDoc.ActiveDoc.Objects.GetObjectList(new ObjectEnumeratorSettings() { HiddenObjects = true}).ToList();
            var unlockedObjects = objectsOutBlock.Where(o => o.IsLocked == false).ToList();
            foreach (var item in unlockedObjects) RhinoDoc.ActiveDoc.Objects.Lock(item.Id, true); // 锁定所有图块外没有锁定的物体
            
            RhinoDoc.ActiveDoc.Views.Redraw();


            // ****显示图块子物体
            RhinoDoc.ActiveDoc.Objects.AddExplodedInstancePieces(block, false); // 该方法直接将一个图块引例的炸开后物体添加到文档中
            RhinoDoc.ActiveDoc.Views.Redraw();


            ///3、打开窗口，并记录原有物体，用来分辨那些是图块编辑的物体，那些是图块外的物体
            var dia = new BlockEditDialog(block, objectsOutBlock.Select(s=>s.Id).ToList(), unlockedObjects.Select(s => s.Id).ToList());
            dia.Owner = Rhino.UI.RhinoEtoApp.MainWindow;
            dia.Show();
        }

        /// <summary>
        /// 对图块按照定义图块的名字分类
        /// </summary>
        /// <param name="blockObjects">要进行分类的图块</param>
        private void ClassifyBlockByDefinition(List<RhinoObject> blockObjects)
        {
            foreach (var item in blockObjects)
            {
                InstanceObject block = item as InstanceObject;
                string blockName = block.InstanceDefinition.Name;
                if (blockTypes.ContainsKey(blockName))
                {
                    blockTypes[blockName].Add(block);
                }
                else
                {
                    blockTypes.Add(blockName, new List<InstanceObject>() { block });
                }
            }
        }

    }

    /// <summary>
    /// 创建一个确认编辑完成的窗口
    /// </summary>
    class BlockEditDialog : Form
    {
        // 记录当前编辑的图块
        public InstanceObject EditedBlock { get; set; }

        // 记录原本不属于图块的物体
        public List<Guid> ObjectsOutBlock { get; set; }

        // 记录原本没有锁定的物体
        public List<Guid> UnlockedObjects { get; set; }

        /// <summary>
        /// 初始化窗口
        /// </summary>
        /// <param name="editedBlock">记录正在编辑的图块</param>
        /// <param name="objects">保存rhino中所有不属于图块的物体</param>
        /// <param name="unlockObjects">记录原本没有锁定，因为编辑图块被锁定的物体</param>
        public BlockEditDialog(InstanceObject editedBlock, List<Guid> objects, List<Guid> unlockObjects)
        {
            // 更新记录数据
            EditedBlock = editedBlock;
            ObjectsOutBlock = objects;
            UnlockedObjects = unlockObjects;

            // 编辑窗口属性
            base.Title = editedBlock.Name;
            base.ClientSize = new Eto.Drawing.Size(200, 200);
            base.Padding = new Eto.Drawing.Padding(10);
            base.Resizable = true;

            // 向窗口添加按钮
            var defaultButton = new Button() { Text = "确定" };
            defaultButton.Click += DefaultButton_Click;

            var cancelButton = new Button() { Text = "取消" };
            cancelButton.Click += CancelButton_Click;

            var layout = new DynamicLayout();
            layout.Spacing = new Eto.Drawing.Size(5, 5);
            layout.AddRow(defaultButton, cancelButton);

            base.Content = layout;

            Closed += CancelButton_Click; // 接管窗口关闭事件，需要做的操作和取消一样
        }

        private void DefaultButton_Click(object sender, EventArgs e)
        {
            BlockEditEnd(true);
            base.Close();
        }

        private void CancelButton_Click(object sender, EventArgs e)
        {
            BlockEditEnd(false);
            base.Close();
        }


        /// <summary>
        /// 图块编辑确认后的后续操作
        /// </summary>
        /// <param name="confirm">确定/取消</param>
        protected void BlockEditEnd(bool confirm)
        {
            // 1、查找属于这个图块的物体
            var allObjects = RhinoDoc.ActiveDoc.Objects.GetObjectList(new ObjectEnumeratorSettings() { HiddenObjects = true }).ToList();
            List<RhinoObject> newObjs = new List<RhinoObject>(allObjects.Count - ObjectsOutBlock.Count);
            var aaa = allObjects.Select(s=>s).ToList();
            foreach (var item in allObjects)
            {
                // 只要不是原有的，就是属于这个图块的
                if (!ObjectsOutBlock.Contains(item.Id)) newObjs.Add(item);
            }
            var a = RhinoDoc.ActiveDoc.Objects.Select(s => s).ToList();
            if (confirm)
            {
                // 当点击了确认时，就重建图块
                // 2、复原变换
                Transform inverseTransform;
                var ifInverse = EditedBlock.InstanceXform.TryGetInverse(out inverseTransform); // 获取变换的逆变换

                for (int i = 0; i < newObjs.Count; i++)
                {
                    var objId = RhinoDoc.ActiveDoc.Objects.Transform(newObjs[i], inverseTransform, true); // 将每一个物体复原，并记录变换后的物体，用于之后删除痕迹
                    newObjs[i] = RhinoDoc.ActiveDoc.Objects.FindId(objId);
                }


                // 3、使用新的图块子物件修改图块定义
                RhinoDoc.ActiveDoc.InstanceDefinitions.ModifyGeometry(EditedBlock.InstanceDefinition.Index, newObjs.Select(s => s.Geometry));
            }

            // 4、痕迹清理：删除编辑后的图块的子物体， 显示当前编辑的图块，解锁因为编辑图块而锁定的物体
            RhinoDoc.ActiveDoc.Objects.Delete(newObjs.Select(s => s.Id), true);

            foreach (var id in UnlockedObjects) RhinoDoc.ActiveDoc.Objects.Unlock(id, true); // 解锁所有物体

            EditedBlock.Attributes.Visible = true;
            EditedBlock.CommitChanges();
            var b = RhinoDoc.ActiveDoc.Objects.Select(s => s).ToList();
            RhinoDoc.ActiveDoc.Views.Redraw(); // 重绘视窗
        }
    }
}
